#!/bin/bash
SYSTEM=${1,,}                                                                        # windows, mugen
ACTION=${2,,}                                                                        # play, stop... see usage
GAMENAME="$3"                                                                        # fullpath to game: e.g. /userdata/roms/windows/Age of Empires.wine
TRICK=${4,,}                                                                         # WINE-tricks, see https://github.com/Winetricks/winetricks
ROMGAMENAME=$(basename "${GAMENAME}")                                                # gamedir or gameexecutable: e.g. "Age of Empires.wine" or "AoE_inst.exe"
ROMBASEDIR=$(dirname "${GAMENAME}")                                                  # gamedir or rootdir: eg "/userdata/roms/windows" if AoE is a wine.dir
GAMEEXT="${GAMENAME##*.}"                                                            # gameextension: pc, wine, exe, msi
# log scripts (aka winserver) running time with bash builtin command
SECONDS=0

## Folders
WINE_BOTTLE_DIR="/userdata/system/wine-bottles/${SYSTEM}"                            # Basestorage for our bottles, more variables in init_wine()
G_ROMS_DIR="/userdata/roms/${SYSTEM}"                                                # Gamesdir for our games, more variables in init_wine()

## WINE-VARS, these need to be prepared in init_wine()
## in general Wine detection routines, for specific game if entered in batocera.conf
G_RESCUR=
WINE_RUNNER=
WINE_VERSION=
## Wine executables, to be populated in init_wine() with find_wine_dir()
## variables heavily depend on DIR-entry, found in init_wine()
DIR=
USER_DIR=
WINE=
WINE64=
WINESERVER=
MSIEXEC=
WINETRICKS=
# Save function carried out in saveFilesToUserdata()
WINE_SAVEDIR=
WINE_SAVEFILES=
SYSTEM_SAVEDIR=
# createAutorunCmd() tries to creates automatically autorun.cmd if you use windows_installs with msi/exe
# a basic exe-filter is here in the script, user can set advanced one, see i and ii in function
AUTORUN_FILEMASK="$4"
AUTORUN_FOUNDEXE=
#AUTORUN_FILTER=
# Use requestFileSystem() to check dirs and files for symlinks and filesystem, it's a small dev-tool for monitoring the entire script
# Usage requestFileSystem "FILE" "DIR" "FILE"

# Global variables for Wayland/X.org handling, SYSTEM_DISPLAY_MODE is in init_wine()
SYSTEM_DISPLAY_MODE=""
CMD_PREFIX=""

stopWineServer() {
    [[ -z "${WINESERVER}" || -z "${WINEPOINT}" ]] && exit 0

    #try to cleanly exit wineserver
    WINEPREFIX=${WINEPOINT} "${WINESERVER}" -k &
    #wait 10s for clean exit
    echo "${FUNCNAME[0]}: waiting wineserver shutdown" >&2
    if waitWineServer 10; then
        echo "${FUNCNAME[0]}: wineserver has cleanly exited" >&2
        return 0
    fi

    #kill all process with wineprefix as envvar if wineserver is still not stopped
    declare -a PIDS

    # find all process with wineprefix environement
    for PID in $(ps -e -o pid=); do
        if grep -z "WINEPREFIX=${WINEPOINT}" "/proc/$PID/environ" 2>/dev/null; then
            PIDS+=("$PID")
        fi
    done

    # kill all pids
    if [ ${#PIDS[@]} -gt 0 ]; then
        echo "${FUNCNAME[0]}: Killing stuck wine processes: ${PIDS[*]}"
        kill -9 "${PIDS[@]}"
    fi
    return 1
}

# Arguments: key, system, game
get_setting() {
    /usr/bin/batocera-settings-get "$2[\"$3\"].$1" "$2.$1" "global.$1"
}

find_wine_dir() {
    local WINE_VERSION="$1"
    # check if we're using a custom wine runner
    if [[ "$WINE_VERSION" != "wine-tkg" && "$WINE_VERSION" != "wine-proton" ]]; then
        # now check if the folder exists
        if [[ -e "/userdata/system/wine/custom/${WINE_VERSION}" ]]; then
            echo "/userdata/system/wine/custom"
        else
            WINE_VERSION="wine-tkg"
            if [[ -e "/usr/wine/${WINE_VERSION}" ]]; then
                echo "/usr/wine"
            else
                return 1
            fi
        fi
    elif [[ -e "/usr/wine/${WINE_VERSION}" ]]; then
        echo "/usr/wine"
    else
        return 1
    fi
}

update_wine_version() {
    local WINE_VERSION="$1"
    if [[ "$WINE_VERSION" != "wine-tkg" && "$WINE_VERSION" != "wine-proton" ]]; then
        if [[ -e "/userdata/system/wine/custom/${WINE_VERSION}" ]]; then
            echo "$WINE_VERSION"
        else
            WINE_VERSION="wine-tkg"
            echo "$WINE_VERSION"
        fi
    else
        echo "$WINE_VERSION"
    fi
}

waitWineServer() {
    local ret=0 pid=$(pgrep -o -f "${WINESERVER}")
    [[ -z "$pid" ]] && { echo "${FUNCNAME[0]}: Called by ${FUNCNAME[1]} no process '${WINESERVER}' found - finished with hotkey?" >&2; return 0; }
    #from: https://unix.stackexchange.com/a/427133
    #timeout will report not 0 if setted value is reached
    echo "${FUNCNAME[0]}: Called by ${FUNCNAME[1]} wait WineServer: [$pid] ${WINESERVER}"
    timeout "$1" tail -q --pid=$pid -f /dev/null
    ret=$?
    echo "${FUNCNAME[0]}: Finished waiting for WineServer [$pid] with errorcode($ret)"
    return $ret
}

wine_options() {
    WINEPOINT=$1
    NTSYNC="$(get_setting ntsync "${SYSTEM}" "${ROMGAMENAME}")"
    PBA="$(get_setting pba "${SYSTEM}" "${ROMGAMENAME}")"
    FSR="$(get_setting fsr "${SYSTEM}" "${ROMGAMENAME}")"
    FPS_LIMIT="$(get_setting fps_limit "${SYSTEM}" "${ROMGAMENAME}")"
    ALLOW_XIM="$(get_setting allow_xim "${SYSTEM}" "${ROMGAMENAME}")"
    NO_WRITE_WATCH="$(get_setting no_write_watch "${SYSTEM}" "${ROMGAMENAME}")"
    FORCE_LARGE_ADRESS="$(get_setting force_large_adress "${SYSTEM}" "${ROMGAMENAME}")"
    HEAP_DELAY_FREE="$(get_setting heap_delay_free "${SYSTEM}" "${ROMGAMENAME}")"
    HIDE_NVIDIA_GPU="$(get_setting hide_nvidia_gpu "${SYSTEM}" "${ROMGAMENAME}")"
    ENABLE_NVAPI="$(get_setting enable_nvapi "${SYSTEM}" "${ROMGAMENAME}")"
    ENABLE_HIDRAW="$(get_setting enable_hidraw "${SYSTEM}" "${ROMGAMENAME}")"
    DXVK_RESET_CACHE="$(get_setting dxvk_reset_cache "${SYSTEM}" "${ROMGAMENAME}")"
    WINE_NTFS="$(get_setting wine_ntfs "${SYSTEM}" "${ROMGAMENAME}")"
    WINE_DEBUG="$(get_setting wine_debug "${SYSTEM}" "${ROMGAMENAME}")"
    KEYBOARD="$(/usr/bin/batocera-settings-get system.kblayout)"
    VIRTUAL_DESKTOP="$(get_setting virtual_desktop "${SYSTEM}" "${ROMGAMENAME}")"
    VIRTUAL_DESKTOP_SIZE="$(get_setting videomode "${SYSTEM}" "${ROMGAMENAME}" || batocera-resolution currentResolution)"
    WAYLAND_DRIVER="$(get_setting wayland_driver "${SYSTEM}" "${ROMGAMENAME}")"

    if [[ "${VIRTUAL_DESKTOP}" = 1 ]]; then
        VDESKTOP="explorer /desktop=Wine,${VIRTUAL_DESKTOP_SIZE}"
    fi

    # Handle Wayland driver option only if the system is running Wayland
    if [[ "${SYSTEM_DISPLAY_MODE}" == "wayland" ]]; then
        if [[ "${WAYLAND_DRIVER}" != "native" ]]; then
            echo "*** System is Wayland, using XWayland by default or as requested. ***"
            setxkbmap "${KEYBOARD}"
        else
            CMD_PREFIX="DISPLAY="
            echo "*** System is Wayland and 'native' driver requested. Forcing Wine native Wayland mode. ***"
        fi
    else
        # System is X.org, run as normal
        echo "*** System is X.org. Running in standard X11 mode. ***"
        setxkbmap "${KEYBOARD}"
    fi

    # ESYNC and FSYNC are deprecated in favor of NTSYNC with modern Wine and kernel.
    # NTSYNC is often enabled by default when supported.
    # The option is provided to explicitly disable it if needed.
    if [ "$NTSYNC" = "0" ]; then
        # Disable ntsync module if loaded
        if lsmod | grep -q ^ntsync; then
            echo "Removing ntsync module..."
            rmmod ntsync
        else
            echo "Ntsync module already not loaded"
        fi
        export WINEDISABLEFASTSYNC=1
        echo "Ntsync disabled for Wine"
    else
        # Enable ntsync module if available and accessible
        if lsmod | grep -q ^ntsync; then
            echo "Ntsync module already loaded"
        else
            echo "Loading ntsync module..."
            modprobe ntsync
        fi

        if [ -e /dev/ntsync ] && [ -r /dev/ntsync ]; then
            export WINEDISABLEFASTSYNC=0
            echo "Ntsync enabled for Wine"
        else
            echo "Warning: /dev/ntsync not accessible after module load, disabling ntsync in Wine"
            export WINEDISABLEFASTSYNC=1
        fi
    fi

    export PBA_ENABLE=0
    [[ "${PBA}" = 1 ]] && PBA_ENABLE=1

    export DXVK_FRAME_RATE=0
    [[ "${FPS_LIMIT}" = 1 ]] && DXVK_FRAME_RATE=60

    export WINEDEBUG="-all"
    [[ "${WINE_DEBUG}" = 1 ]] && WINEDEBUG="err+all,fixme+all"

    export DXVK_LOG_LEVEL=none
    [[ "${WINE_DEBUG}" = 1 ]] && unset DXVK_LOG_LEVEL

    export VKD3D_DEBUG=none
    [[ "${WINE_DEBUG}" = 1 ]] && unset VKD3D_DEBUG

    export VKD3D_SHADER_CACHE_PATH="/userdata/system/cache"

    export WINE_ENABLE_HIDRAW=0
    [[ "${ENABLE_HIDRAW}" = 1 ]] && WINE_ENABLE_HIDRAW=1

    export WINE_FULLSCREEN_FSR=0
    [[ "${FSR}" = 1 ]] && unset WINE_FULLSCREEN_FSR

    # Wine-mono override for FNA games
    export WINE_MONO_OVERRIDES="Microsoft.Xna.Framework.*,Gac=n"

    # Disable XIM support until libx11 >= 1.7 is widespread
    export WINE_ALLOW_XIM=0
    [[ "${ALLOW_XIM}" = 1 ]] && WINE_ALLOW_XIM=1

    # Advanced options from proton
    export WINE_DISABLE_WRITE_WATCH=0
    [[ "${NO_WRITE_WATCH}" = 1 ]] && WINE_DISABLE_WRITE_WATCH=1

    export WINE_LARGE_ADDRESS_AWARE=0
    [[ "${FORCE_LARGE_ADRESS}" = 1 ]] && WINE_LARGE_ADDRESS_AWARE=1

    export WINE_HEAP_DELAY_FREE=0
    [[ "${HEAP_DELAY_FREE}" = 1 ]] && WINE_HEAP_DELAY_FREE=1

    export WINE_HIDE_NVIDIA_GPU=0
    [[ "${HIDE_NVIDIA_GPU}" = 1 ]] && WINE_HIDE_NVIDIA_GPU=1

    export NVAPI=0
    [[ "${ENABLE_NVAPI}" = 1 ]] && NVAPI=1

    export DXVK_STATE_CACHE=1
    [[ "${DXVK_RESET_CACHE}" = 1 ]] && DXVK_STATE_CACHE=reset

    export NTFS_MODE=0
    [[ "${WINE_NTFS}" = 1 ]] && NTFS_MODE=1

    export STAGING_SHARED_MEMORY=1
    export ULIMIT_SIZE=1048576
    export USE_BUILTIN_VKD3D=0

    # Nvidia variables
    if [[ -e "/userdata/system/cache" ]]; then
        export XDG_CACHE_HOME="/userdata/system/cache"
    else
        mkdir -p "/userdata/system/cache"
        export XDG_CACHE_HOME="/userdata/system/cache"
    fi
    export __GL_SHADER_DISK_CACHE_SIZE=2147483648
    export __GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1
    export __GL_SHADER_DISK_CACHE_PATH="${XDG_CACHE_HOME}"/nvidia

    # While esync is deprecated, the ulimit is still important for Wine performance.
    if ! ulimit -n "${ULIMIT_SIZE}" 2>/dev/null; then
        echo "Warning: could not set file limit, which may impact performance." >&2
    fi
}

redist_install() {
    WINEPREFIX=$1
    local i; local ii
    if [[ -d "${USER_DIR}/exe" ]]; then
        readarray -t i < <(find "${USER_DIR}/exe" -maxdepth 1 -type f -iname "*.exe" -printf "%p\n")
        [[ "${#i[@]}" -eq 0 ]] && { rmdir "${USER_DIR}/exe"; echo "${FUNCNAME[0]}: Place only exe-files to ${USER_DIR}/exe" >&2; return 0; }
        mkdir -p "${USER_DIR}/installed.exe"

        for file in "${i[@]}"; do
            #We compare base-filename in lowercase only and execute the file stored in array
            ii="$(basename "$file")"; ii="${ii,,}"
            echo "Executing file $file"
            case "${ii}" in

            "dxsetup.exe")
                WINEPREFIX=${WINEPOINT} ${CMD_PREFIX} "${WINE}" "${file}" /silent &>/dev/null || return 1
                "${WINESERVER}" -w
            ;;

            "vcredist_x64_2005.exe" | "vcredist_x86_2005.exe" | \
            "vcredist_x64_2008.exe" | "vcredist_x86_2008.exe")
                WINEPREFIX=${WINEPOINT} ${CMD_PREFIX} "${WINE}" "${file}" /q &>/dev/null || return 1
                "${WINESERVER}" -w
            ;;

            "vcredist_x64_2010.exe" | "vcredist_x86_2010.exe" | \
            "vcredist_x64_2012.exe" | "vcredist_x86_2012.exe" | \
            "vcredist_x64_2013.exe" | "vcredist_x86_2013.exe" | \
            "vcredist_x64_2015.exe" | "vcredist_x86_2015.exe" | \
            "vcredist_x64_2017.exe" | "vcredist_x86_2017.exe" | \
            "vcredist_x64_2019.exe" | "vcredist_x86_2019.exe")
                WINEPREFIX=${WINEPOINT} ${CMD_PREFIX} "${WINE}" "${file}" /quiet /qn /norestart &>/dev/null || return 1
                "${WINESERVER}" -w
            ;;

            "vcredist_x64_2015_2019.exe" | "vcredist_x86_2015_2019.exe" | \
            "vcredist_x64_2015_2022.exe" | "vcredist_x86_2015_2022.exe" )
                WINEPREFIX=${WINEPOINT} ${CMD_PREFIX} "${WINE}" "${file}" /quiet /qn /norestart &>/dev/null || return 1
                "${WINESERVER}" -w
            ;;

            "oalinst.exe")
                WINEPREFIX=${WINEPOINT} ${CMD_PREFIX} "${WINE}" "${file}" /s &>/dev/null
                "${WINESERVER}" -w
            ;;

            *)
                WINEPREFIX=${WINEPOINT} ${CMD_PREFIX} "${WINE}" "${file}" &>/dev/null
                "${WINESERVER}" -w
            ;;
            esac
            echo "$(date '+%D %T') - Installed: '$file' --> ${WINEPREFIX}" | tee -a "${USER_DIR}/imports.log"
            mv --backup=t "$file" "${USER_DIR}/installed.exe"
        done
        rmdir "${USER_DIR}/exe" || { echo "${FUNCNAME[0]}: Place only exe-files to ${USER_DIR}/exe" >&2; }
    fi
    return 0
}

msi_install() {
    WINEPREFIX=$1
    local i; local ii
    ii="${USER_DIR}/msi"
    if [[ -d "$ii" ]]; then
        readarray -t i < <(find "$ii" -maxdepth 1 -type f -iname "*.msi" -printf "%p\n")
        [[ "${#i[@]}" -eq 0 ]] && { rmdir "$ii"; echo "${FUNCNAME[0]}: Place only msi-files to $ii" >&2; return 0; }
        mkdir -p "${USER_DIR}/installed.msi"

        for file in "${i[@]}"; do
            echo "Executing file $file"
            WINEPREFIX=${WINEPOINT} ${CMD_PREFIX} "${MSIEXEC}" -i "${file}" /quiet /qn /norestart &>/dev/null || return 1
            "${WINESERVER}" -w
            echo "$(date '+%D %T') - Installed: '$file' --> $WINEPOINT" | tee -a "${USER_DIR}/imports.log"
            mv --backup=t "$file" "${USER_DIR}/installed.msi"
        done
        rmdir "$ii" || { echo "${FUNCNAME[0]}: Place only msi-files to $ii" >&2; }
    fi
    return 0
}

reg_install() {
    WINEPREFIX=$1
    local i; local ii
    ii="${USER_DIR}/regs"
    if [[ -e "/var/run/rawinput.reg" ]]; then
        WINEPREFIX=${WINEPOINT} ${CMD_PREFIX} "${WINE}" regedit //?/unix/var/run/rawinput.reg &>/dev/null || return 1
        WINEPREFIX=${WINEPOINT} ${CMD_PREFIX} "${WINE64}" regedit //?/unix/var/run/rawinput.reg &>/dev/null || return 1
        rm /var/run/rawinput.reg
    fi

    if [[ -d "$ii" ]]; then
        readarray -t i < <(find "$ii" -maxdepth 1 -type f -iname "*.reg" -printf "%p\n")
        [[ "${#i[@]}" -eq 0 ]] && { rmdir "$ii"; echo "${FUNCNAME[0]}: Place only reg-files to $ii" >&2; return 0; }
        mkdir -p "${USER_DIR}/installed.regs"

        for file in "${i[@]}"; do
            WINEPREFIX=${WINEPOINT} ${CMD_PREFIX} "${WINE}" regedit //?/unix"${file}" &>/dev/null || return 1
            WINEPREFIX=${WINEPOINT} ${CMD_PREFIX} "${WINE64}" regedit //?/unix"${file}" &>/dev/null || return 1
            echo "$(date '+%D %T') - Imported: '$file' --> $WINEPOINT" | tee -a "${USER_DIR}/imports.log"
            mv --backup=t "$file" "${USER_DIR}/installed.regs"
        done
        rmdir  "$ii" || { echo "${FUNCNAME[0]}: Place only reg-files to $ii" >&2; }
    fi

    if [[ "${WINE_ENABLE_HIDRAW}" = 1 ]]; then
        if ! grep -q "\"DisableHidraw\"=dword:00000000" "${WINEPOINT}/system.reg"; then
            WINEPREFIX=${WINEPOINT} ${CMD_PREFIX} "${WINE}" reg add "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\winebus" /v "DisableHidraw" /t REG_DWORD /d 0 /f
            waitWineServer 0
        fi
    else
        if ! grep -q "\"DisableHidraw\"=dword:00000001" "${WINEPOINT}/system.reg"; then
            WINEPREFIX=${WINEPOINT} ${CMD_PREFIX} "${WINE}" reg add "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\winebus" /v "DisableHidraw" /t REG_DWORD /d 1 /f
            waitWineServer 0
        fi
    fi
    return 0
}

fonts_install() {
    WINEPREFIX=$1
    local i; local ii
    ii="${USER_DIR}/fonts"
    if [[ -d "$ii" ]]; then
        readarray -t i < <(find "$ii" -maxdepth 1 -type f \( -iname "*.ttf" -o -iname "*.ttc" \) -printf "%p\n")
        [[ "${#i[@]}" -eq 0 ]] && { rmdir "$ii"; echo "${FUNCNAME[0]}: Place only ttc or ttf-files to $ii" >&2; return 0; }
        mkdir -p "${USER_DIR}/installed.fonts"

        for file in "${i[@]}"; do
            cp -i "$file" "${WINEPREFIX}/drive_c/windows/Fonts" || return 1
            echo "$(date '+%D %T') - Imported: '$file' --> ${WINEPREFIX}/drive_c/windows/Fonts" | tee -a "${USER_DIR}/imports.log"
            mv --backup=t "$file" "${USER_DIR}/installed.fonts"
        done
        rmdir "$ii" || { echo "${FUNCNAME[0]}: Place only ttf and ttc-files to $ii" >&2; }
    fi
    return 0
}

dxvk_install() {
    export WINEDLLOVERRIDES="winemenubuilder.exe="
    WINEPREFIX=$1

    # install dxvk only on system where it is available (aka, not x86)
    [[ -e "/usr/wine/dxvk" ]] || return 0

    DXVK=$(get_setting dxvk "${SYSTEM}" "${ROMGAMENAME}")
    DXVK_HUD=$(get_setting dxvk_hud "${SYSTEM}" "${ROMGAMENAME}")

    if [[ "${DXVK_HUD}" = 1 ]]; then
	    export DXVK_HUD=1
    fi

    if [[ "${DXVK}" = 1 ]]; then
        mkdir -p "${WINEPREFIX}/drive_c/windows/system32" "${WINEPREFIX}/drive_c/windows/syswow64" || return 1
        if [[ -e "/userdata/system/wine/dxvk" ]]; then
            echo "Creating links using /userdata, Linux File System required !!!"
            ln -sf "/userdata/system/wine/dxvk/x64/"{d3d12.dll,d3d12core.dll,d3d11.dll,d3d10core.dll,d3d9.dll,dxgi.dll,nvapi64.dll} "${WINEPREFIX}/drive_c/windows/system32" || return 1
            ln -sf "/userdata/system/wine/dxvk/x32/"{d3d8.dll,d3d12.dll,d3d12core.dll,d3d11.dll,d3d10core.dll,d3d9.dll,dxgi.dll,nvapi.dll} "${WINEPREFIX}/drive_c/windows/syswow64" || return 1
        else
            echo "Creating links using /usr/wine/dxvk/, Linux File System required !!!"
            ln -sf "/usr/wine/dxvk/x64/"{d3d12.dll,d3d12core.dll,d3d11.dll,d3d10core.dll,d3d9.dll,dxgi.dll,nvapi64.dll} "${WINEPREFIX}/drive_c/windows/system32" || return 1
            ln -sf "/usr/wine/dxvk/x32/"{d3d8.dll,d3d12.dll,d3d12core.dll,d3d11.dll,d3d10core.dll,d3d9.dll,dxgi.dll,nvapi.dll} "${WINEPREFIX}/drive_c/windows/syswow64" || return 1
        fi
    else
        mkdir -p "${WINEPREFIX}/drive_c/windows/system32" "${WINEPREFIX}/drive_c/windows/syswow64" || return 1
        echo "Creating links using ${DIR}/${WINE_VERSION}, Linux File System required !!!"
        ln -sf "${WINE_LIB64_DIR}/x86_64-windows/"{d3d8.dll,d3d12.dll,d3d12core.dll,d3d11.dll,d3d10core.dll,d3d9.dll,dxgi.dll} "${WINEPREFIX}/drive_c/windows/system32" || return 1
        ln -sf "${WINE_LIB32_DIR}/i386-windows/"{d3d8.dll,d3d12.dll,d3d12core.dll,d3d11.dll,d3d10core.dll,d3d9.dll,dxgi.dll} "${WINEPREFIX}/drive_c/windows/syswow64" || return 1
    fi

    if [[ "${DXVK}" = 1 ]]; then
        export DXVK_ASYNC=1
        export DXVK_CONFIG_FILE="/userdata/system/wine/dxvk.conf"
        export WINEDLLOVERRIDES="${WINEDLLOVERRIDES};dxgi,d3d8,d3d9,d3d10core,d3d11,d3d12,d3d12core=n"
        export DXVK_STATE_CACHE_PATH="/userdata/system/cache"
    else
        export DXVK_ASYNC=0
        export WINEDLLOVERRIDES="${WINEDLLOVERRIDES};dxgi,d3d8,d3d9,d3d10core,d3d11,d3d12,d3d12core=b"
    fi

    if [[ "${NVAPI}" = 1 ]]; then
        export DXVK_ENABLE_NVAPI=1
        export WINEDLLOVERRIDES="${WINEDLLOVERRIDES};nvapi,nvapi64=n"
    else
        export DXVK_ENABLE_NVAPI=0
        export WINEDLLOVERRIDES="${WINEDLLOVERRIDES};nvapi64,nvapi="
    fi

    return 0
}

winedevices_setup() {
    WINEPREFIX=$1

    WINEDEVICES=$(get_setting winedevices "${SYSTEM}" "${ROMGAMENAME}")

    for XSYSPATH in system32 syswow64
    do
	if [[ "${WINEDEVICES}" = 1 ]]; then
	    if test ! -e "${WINEPREFIX}/drive_c/windows/${XSYSPATH}/winedevice.exe"
	    then
		if test -e "${WINEPREFIX}/drive_c/windows/${XSYSPATH}/winedevice.exe.wine"
		then
		    cp "${WINEPREFIX}/drive_c/windows/${XSYSPATH}/winedevice.exe.wine" "${WINEPREFIX}/drive_c/windows/${XSYSPATH}/winedevice.exe" || return 1
		fi
	    fi
	else
	    # disable by default (causing issues on dmd, guns, (usb tty devices)) when windevice exits
	    # ps -> root     16126     1  0 11:58 ttyUSB0  00:00:00 C:\windows\system32\winedevice.exe
	    if test -e "${WINEPREFIX}/drive_c/windows/${XSYSPATH}/winedevice.exe"
	    then
		mv "${WINEPREFIX}/drive_c/windows/${XSYSPATH}/winedevice.exe" "${WINEPREFIX}/drive_c/windows/${XSYSPATH}/winedevice.exe.wine" || return 1
	    fi
	fi
    done

    return 0
}

sandboxing_prefix() {
    if [[ -d "${WINEPREFIX}/drive_c/users/steamuser" ]]; then
        USERNAME=steamuser
    fi

    if [[ -d "${WINEPREFIX}/drive_c/users/root" ]]; then
        USERNAME=root
    fi

    echo "Remove Symblink"
    # replace some links by folders.
    # don't create all folders in case links doesn't exist to not create both Music and My Music at the same time (old wine uses My Music, new wine uses Musics)
    local DIR=""

    for DIR in "Downloads" "Documents" "My Documents" "Music" "My Music" "Pictures" "My Pictures" "Videos" "My Videos" "Templates"
    do
        if [[ -L "${WINEPREFIX}/drive_c/users/${USERNAME}/${DIR}" ]]; then
            unlink "${WINEPREFIX}/drive_c/users/${USERNAME}/${DIR}" || return 1
            mkdir -p "${WINEPREFIX}/drive_c/users/${USERNAME}/${DIR}" || return 1
        fi
    done

    return 0
}

saveFilesToUserdata() {
    SYSTEM_SAVEDIR="/userdata/saves/${SYSTEM}/${1%.*}"
    WINE_SAVEDIR="$2"
    WINE_SAVEFILES="$3"

    [[ -z "${WINE_SAVEDIR}" && -z "${WINE_SAVEFILES}" ]] && return 0 #No argument in autorun.cmd -> return

    #Prepare for Savegames Files or Folders, SYSTEM_SAVEDIR and WINE_SAVEDIR are needed here
    mkdir -p "${SYSTEM_SAVEDIR}"                                         #Create dir to store our savegames in /userdata
    mkdir -p "${WINEPOINT}/$(dirname "${WINE_SAVEDIR}")"                 #Create SAVEGAME_ROOT_DIR from there we can cp the files
    requestFileSystem "${SYSTEM_SAVEDIR}" "${WINEPOINT}/$(dirname "${WINE_SAVEDIR}")" "${WINEPOINT}/${WINE_SAVEDIR}" #Check filesystem
    pushd "${WINEPOINT}/$(dirname "${WINE_SAVEDIR}")" >/dev/null         #Enter SAVEGAME_ROOT_DIR
    WINE_SAVEDIR="$(basename "${WINE_SAVEDIR}")"                         #Savegamedir itself, content is copied/linked
    echo "Preparing Savefiles: WINE_SAVEDIR: ${WINE_SAVEDIR} -> SYSTEM_SAVEDIR: ${SYSTEM_SAVEDIR}"

    if [[ -z "${WINE_SAVEFILES}" ]]; then
        #Copy existing savedir content and link whole wine_savedir directory, check if dir is a symlink or not
        if [[ ! -L "${WINE_SAVEDIR}" && -d "${WINE_SAVEDIR}" ]]; then
            cp -r "${WINE_SAVEDIR}/." "${SYSTEM_SAVEDIR}" || { echo "${FUNCNAME[0]}: Error in copying files ${WINE_SAVEDIR} -> ${SYSTEM_SAVEDIR}" >&2; return 1; }
            rm -rf "${WINE_SAVEDIR}"
        fi
        [[ ! -L "${WINE_SAVEDIR}" ]] && ln -s "${SYSTEM_SAVEDIR}" "${WINE_SAVEDIR}"
        popd >/dev/null

    elif [[ -n "${WINE_SAVEFILES}" ]]; then
        mkdir "${WINE_SAVEDIR}"
        pushd "${WINE_SAVEDIR}" >/dev/null                               #You are working inside the game saves dir
        #split the files list to array with ; delimiter
        IFS=';' read -r -a SAVEFILES_ARRAY <<< "${WINE_SAVEFILES}"
        for SAVEFILE in "${SAVEFILES_ARRAY[@]}"; do
            if [[ -e "${SAVEFILE}" ]]; then
                #if savefiles exist in the prefix and not yet in system save, move it once
                if [[ ! -e "${SYSTEM_SAVEDIR}/${SAVEFILE}" && ! -L "${SAVEFILE}" ]]; then
                    mv -f "${SAVEFILE}" "${SYSTEM_SAVEDIR}/${SAVEFILE}"
                else
                    rm "${SAVEFILE}"
                fi
            fi
            ln -sf "${SYSTEM_SAVEDIR}/${SAVEFILE}" "${SAVEFILE}"
        done
        popd >/dev/null; popd >/dev/null #Clear stack
    fi
}

createWineDirectory() {
    WINEPREFIX=$1

    # already created
    [[ -e "${WINEPREFIX}" ]] && return 0

    # please wait screen
    bsod-wine&
    BSODPID=$!

    mkdir -p "${WINEPREFIX}" || return 1

    # Workaround wine bottle creation issue with debug enabled
    export WINEDLLOVERRIDES="winegstreamer="

    if ! WINEPREFIX=${WINEPREFIX} ${CMD_PREFIX} "${WINE}" hostname; then
	    rm -rf "${WINEPREFIX}"
	    kill -15 "${BSODPID}"
        echo "+++ Failed initialising ${WINEPREFIX} +++"
	    return 1
    fi

    kill -15 "${BSODPID}"

    return 0
}

getWine_var() {
    WINEPOINT=$1
    WINEVAR=$2
    WINEVALUE=$3

    if [[ -e "${WINEPOINT}/autorun.cmd" ]]; then
        VAL=$(cat "${WINEPOINT}/autorun.cmd" | dos2unix | grep "^${WINEVAR}=" | sed -e s+"^${WINEVAR}="+""+ | head -1)
        if [[ -n "${VAL}" ]]; then
            echo "${VAL}"
        else
            echo "${WINEVALUE}"
        fi
    else
        echo "${WINEVALUE}"
    fi
}

play_wine() {
    echo "play_wine"
    GAMENAME="$1"
    WINEPOINT="$2"

    wine_options "${WINEPOINT}"
    redist_install "${WINEPOINT}" || return 1
    msi_install "${WINEPOINT}" || return 1
    reg_install "${WINEPOINT}" || return 1
    fonts_install "${WINEPOINT}" || return 1
    sandboxing_prefix "${WINEPOINT}" || return 1
    dxvk_install "${WINEPOINT}" || return 1
    winedevices_setup "${WINEPOINT}" || return 1

    WINE_CMD=$(getWine_var "${WINEPOINT}" "CMD" "explorer")
    WINE_DIR=$(getWine_var "${WINEPOINT}" "DIR" "")
    WINE_LANG=$(getWine_var "${WINEPOINT}" "LANG" "")
    WINE_ENV=$(getWine_var "${WINEPOINT}" "ENV" "")
    WINE_SAVEDIR=$(getWine_var "${WINEPOINT}" "SAVEDIR" "")
    WINE_SAVEFILES=$(getWine_var "${WINEPOINT}" "SAVEFILES" "")
    saveFilesToUserdata "${ROMGAMENAME}" "${WINE_SAVEDIR}" "${WINE_SAVEFILES}" || return 1

    if [[ -n "${WINE_LANG}" ]]; then
        (cd "${WINEPOINT}/${WINE_DIR}" && LC_ALL=${WINE_LANG} WINEPREFIX=${WINEPOINT} eval "${WINE_ENV}" ${CMD_PREFIX} "${WINE}" ${VDESKTOP} ${WINE_CMD})
    else
        (cd "${WINEPOINT}/${WINE_DIR}" && WINEPREFIX=${WINEPOINT} eval "${WINE_ENV}" ${CMD_PREFIX} "${WINE}" ${VDESKTOP} ${WINE_CMD})
    fi
    waitWineServer 0
}

play_pc() {
    echo "play_pc"
    GAMENAME="$1"
    WINEPOINT="$2"

    wine_options "${WINEPOINT}"
    createWineDirectory "${WINEPOINT}" || return 1
    redist_install "${WINEPOINT}" || return 1
    msi_install "${WINEPOINT}" || return 1
    reg_install "${WINEPOINT}" || return 1
    fonts_install "${WINEPOINT}" || return 1
    sandboxing_prefix "${WINEPOINT}" || return 1
    dxvk_install "${WINEPOINT}" || return 1
    winedevices_setup "${WINEPOINT}" || return 1

    WINE_CMD=$(getWine_var "${GAMENAME}" "CMD" "explorer")
    WINE_DIR=$(getWine_var "${GAMENAME}" "DIR" "")
    WINE_LANG=$(getWine_var "${GAMENAME}" "LANG" "")
    WINE_ENV=$(getWine_var "${GAMENAME}" "ENV" "")
    WINE_SAVEDIR=$(getWine_var "${GAMENAME}" "SAVEDIR" "")
    WINE_SAVEFILES=$(getWine_var "${GAMENAME}" "SAVEFILES" "")
    saveFilesToUserdata "${ROMGAMENAME}" "${WINE_SAVEDIR}" "${WINE_SAVEFILES}" || return 1

    env
    if [[ -n "${WINE_LANG}" ]]; then
        (cd "${GAMENAME}/${WINE_DIR}" && LC_ALL=${WINE_LANG} WINEPREFIX=${WINEPOINT} eval "${WINE_ENV}" ${CMD_PREFIX} "${WINE}" ${VDESKTOP} ${WINE_CMD})
    else
        (cd "${GAMENAME}/${WINE_DIR}" && WINEPREFIX=${WINEPOINT} eval "${WINE_ENV}" ${CMD_PREFIX} "${WINE}" ${VDESKTOP} ${WINE_CMD})
    fi
    waitWineServer 0
}

trick_wine() {
    GAMENAME="$1"
    WINEPOINT="$2"
    TRICK="$3"

    if [[ -e "${WINETRICKS}" ]]; then
        echo "Winetricks is installed"
    else
        echo "Winetricks is downloading"
        wget -O "${WINETRICKS}" "https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks" &>/dev/null
        chmod +x "${WINETRICKS}"
        echo "Winetricks is now installed"
    fi
    WINEPREFIX=${WINEPOINT} ${CMD_PREFIX} "${WINETRICKS}" "${TRICK}"
}

#play_iso() {
#    GAMENAME=$1
#    # TODO
#}

play_exe() {
    GAMENAME="$1"
    WINEPOINT="$2"

    wine_options "${WINEPOINT}"
    createWineDirectory "${WINEPOINT}" || return 1
    redist_install "${WINEPOINT}" || return 1
    msi_install "${WINEPOINT}" || return 1
    reg_install "${WINEPOINT}" || return 1
    fonts_install "${WINEPOINT}" || return 1
    sandboxing_prefix "${WINEPOINT}" || return 1
    dxvk_install "${WINEPOINT}" || return 1
    winedevices_setup "${WINEPOINT}" || return 1

    (cd "${ROMBASEDIR}" && WINEPREFIX=${WINEPOINT} ${CMD_PREFIX} "${WINE}" "${ROMGAMENAME}")
    waitWineServer 0
}

play_winetgz() {
    echo "play_winetgz"
    GAMENAME="$1"
    WINEPOINT="$2"

    wine_options "${WINEPOINT}"
    if [[ ! -e "${WINEPOINT}" ]]; then
	    mkdir -p "${WINEPOINT}" || return 1
	    (cd "${WINEPOINT}" && gunzip -c "${GAMENAME}" | tar xf -) || return 1
    fi

    redist_install "${WINEPOINT}" || return 1
    msi_install "${WINEPOINT}" || return 1
    reg_install "${WINEPOINT}" || return 1
    fonts_install "${WINEPOINT}" || return 1
    sandboxing_prefix "${WINEPOINT}" || return 1
    dxvk_install "${WINEPOINT}" || return 1
    winedevices_setup "${WINEPOINT}" || return 1

    WINE_CMD=$(getWine_var "${WINEPOINT}" "CMD" "explorer")
    WINE_DIR=$(getWine_var "${WINEPOINT}" "DIR" "")
    WINE_LANG=$(getWine_var "${WINEPOINT}" "LANG" "")
    WINE_ENV=$(getWine_var "${WINEPOINT}" "ENV" "")
    WINE_SAVEDIR=$(getWine_var "${WINEPOINT}" "SAVEDIR" "")
    WINE_SAVEFILES=$(getWine_var "${WINEPOINT}" "SAVEFILES" "")
    saveFilesToUserdata "${ROMGAMENAME}" "${WINE_SAVEDIR}" "${WINE_SAVEFILES}" || return 1

    if [[ -n "${WINE_LANG}" ]]; then
        (cd "${WINEPOINT}/${WINE_DIR}" && LC_ALL=${WINE_LANG} WINEPREFIX=${WINEPOINT} eval "${WINE_ENV}" ${CMD_PREFIX} "${WINE}" ${VDESKTOP} ${WINE_CMD})
    else
        (cd "${WINEPOINT}/${WINE_DIR}" && WINEPREFIX=${WINEPOINT} eval "${WINE_ENV}" ${CMD_PREFIX} "${WINE}" ${VDESKTOP} ${WINE_CMD})
    fi
    waitWineServer 0
}

# Function to safely unmount a mount point with multiple attempts
safe_umount() {
    local mount_point="$1"
    local attempts=3

    for ((i=1; i<=attempts; i++)); do
        # Try lazy unmount first
        umount -l "${mount_point}" && return 0
        # If lazy unmount fails, try force unmount
        umount -f "${mount_point}" && return 0
        # If that fails, try recursive force unmount
        umount -f -R "${mount_point}" && return 0
        sleep 1
    done

    # If all attempts fail, we return an error
    echo "Error: Failed to unmount ${mount_point} after ${attempts} attempts" >&2
    return 1
}

play_squashfs() {
    echo "play_squashfs"
    GAMENAME="$1"
    WINEPOINT="$2"
    wine_options "${WINEPOINT}"
    SQUASHFSPOINT="/var/run/wine/squashfs_${ROMGAMENAME}"
    SAVEPOINT="$3"
    WORKPOINT="$4"

    # Attempt to unmount any existing mount points before starting
    [[ -d "${WINEPOINT}" ]] && safe_umount "${WINEPOINT}"
    [[ -d "${SQUASHFSPOINT}" ]] && safe_umount "${SQUASHFSPOINT}"

    # Remove directories if they exist
    [[ -d "${SQUASHFSPOINT}" ]] && rm -rf "${SQUASHFSPOINT}"
    [[ -d "${WORKPOINT}" ]] && rm -rf "${WORKPOINT}"
    [[ -d "${WINEPOINT}" ]] && rm -rf "${WINEPOINT}"

    # Create necessary fresh directories
    mkdir -p "${SAVEPOINT}" || return 1
    mkdir -p "${WORKPOINT}" || return 1
    mkdir -p "${WINEPOINT}" || return 1
    mkdir -p "${SQUASHFSPOINT}" || return 1

    # Mount squashfs
    if ! mount "${GAMENAME}" "${SQUASHFSPOINT}"; then
        [[ -d "${SQUASHFSPOINT}" ]] && rm -rf "${SQUASHFSPOINT}"
        [[ -d "${WORKPOINT}" ]] && rm -rf "${WORKPOINT}"
        [[ -d "${WINEPOINT}" ]] && rm -rf "${WINEPOINT}"
        return 1
    fi

    # Mount overlay
    if ! mount -t overlay -o rw,lowerdir="${SQUASHFSPOINT}",upperdir="${SAVEPOINT}",workdir="${WORKPOINT}",redirect_dir=on overlay "${WINEPOINT}"; then
        safe_umount "${SQUASHFSPOINT}"
        [[ -d "${SQUASHFSPOINT}" ]] && rm -rf "${SQUASHFSPOINT}"
        [[ -d "${WORKPOINT}" ]] && rm -rf "${WORKPOINT}"
        [[ -d "${WINEPOINT}" ]] && rm -rf "${WINEPOINT}"
        return 1
    fi

    reg_install "${WINEPOINT}" || return 1
    fonts_install "${WINEPOINT}" || return 1
    dxvk_install "${WINEPOINT}" || return 1
    winedevices_setup "${WINEPOINT}" || return 1

    WINE_CMD=$(getWine_var "${WINEPOINT}" "CMD" "explorer")
    WINE_DIR=$(getWine_var "${WINEPOINT}" "DIR" "")
    WINE_LANG=$(getWine_var "${WINEPOINT}" "LANG" "")
    WINE_ENV=$(getWine_var "${WINEPOINT}" "ENV" "")
    WINE_SAVEDIR=$(getWine_var "${WINEPOINT}" "SAVEDIR" "")
    WINE_SAVEFILES=$(getWine_var "${WINEPOINT}" "SAVEFILES" "")
    saveFilesToUserdata "${ROMGAMENAME}" "${WINE_SAVEDIR}" "${WINE_SAVEFILES}" || return 1

    if [[ -n "${WINE_LANG}" ]]; then
        (cd "${WINEPOINT}/${WINE_DIR}" && LC_ALL=${WINE_LANG} WINEPREFIX=${WINEPOINT} eval "${WINE_ENV}" ${CMD_PREFIX} "${WINE}" ${VDESKTOP} ${WINE_CMD})
    else
        (cd "${WINEPOINT}/${WINE_DIR}" && WINEPREFIX=${WINEPOINT} eval "${WINE_ENV}" ${CMD_PREFIX} "${WINE}" ${VDESKTOP} ${WINE_CMD})
    fi

    waitWineServer 0
}

createAutorunCmd() {
    WINEPOINT="$1"
    AUTORUN_FILEMASK="$2"
    local i; local ii

    #Improved version of creating a CMD-file - the FILTER array can handle RegEx like * and ? - grep strips every line with a #-literal
    #Using RexEx extensions from user created files, ~/../roms/windows_installers is prefered over ~/../saves/windows_installers
    i="/userdata/roms/windows_installers/autorun-regex.txt"
    ii="/userdata/saves/windows_installers/autorun-regex.txt"
    [[ -e "$i" ]] || { [[ -e "$ii" ]] && i="$ii"; } && { dos2unix -k -q "$i"; readarray -t AUTORUN_FILTER < <(grep -v -E "^[[:space:]*]|[#]|^[[:space:]]*$" "$i"); }
    # Pre-setted filter to exclude some standard files from created WINEPREFIX
    AUTORUN_FILTER+=("^.*/Windows Media Player/.*$" "^.*/Windows NT/.*$" "^.*/Internet Explorer/.*$" "^.*/drive_c/windows/.*$"
                     "^.*/[Uu]nins[[:alnum:]]{0,6}\.exe$" "^.*/[Ii]nstall(..)?\.exe$" "^.*/[Ss]etup\.exe$" "^.*/[Uu]nwise(..)?\.exe$")

    #We search in WINEPOINT dir for exes and I assume it's somewhere installed in Program Files, or Program Files(x86)
    pushd "$WINEPOINT" > /dev/null
    readarray -t AUTORUN_FOUNDEXE < <(find ${AUTORUN_FILEMASK} -type f -iname "*.exe" -printf "%p\n" | sort -n)

    for i in "${AUTORUN_FILTER[@]}"; do
        for ii in "${!AUTORUN_FOUNDEXE[@]}"; do
            [[ "${AUTORUN_FOUNDEXE[$ii]}"  =~ $i ]] && unset AUTORUN_FOUNDEXE[$ii]
        done
    done

    AUTORUN_FOUNDEXE=("${AUTORUN_FOUNDEXE[@]}") #Renew array after unset elements
    unset AUTORUN_FILTER
    popd > /dev/null
    [[ "${FUNCNAME[1]}" == "main" ]] && return 0 #Don't create autorun.cmd if used parameter "autorun"

    if [[ ${#AUTORUN_FOUNDEXE[@]} -eq 1 ]]; then
        (
            echo "DIR=$(dirname "${AUTORUN_FOUNDEXE[0]}")"
            echo "CMD=\"$(basename "${AUTORUN_FOUNDEXE[0]}")\""
        ) > "${WINEPOINT}/autorun.cmd"
    else
       (
            echo "#DIR=drive_c/Program Files/myprogram"
            echo "#CMD=start.exe"
        ) > "${WINEPOINT}/autorun.cmd"
    fi
    return 0
}

install_exe_msi() {
    #We need to select annother install type for MSI
    #ROMBASEDIR is here /userdata/roms/windows_installer and ROMGAMENAME is the executable only
    GAMEEXT="$1"
    GAMENAME="$2"
    WINEPOINT="$3"
    createWineDirectory "${WINEPOINT}"
    [[ "${GAMEEXT}" == "exe" ]] && WINEPREFIX=${WINEPOINT} ${CMD_PREFIX} "${WINE}" "${GAMENAME}"
    [[ "${GAMEEXT}" == "msi" ]] && WINEPREFIX=${WINEPOINT} ${CMD_PREFIX} "${MSIEXEC}" -i "${GAMENAME}"
    waitWineServer 0
    createAutorunCmd "${WINEPOINT}" "drive_c/P*"
}

install_iso() {
    GAMENAME="$1"
    WINEPOINT="$2"
    GAMEISOMOUNT="/var/run/wine/${ROMGAMENAME}.cdrom"

    mkdir -p "${GAMEISOMOUNT}" || return 1
    if ! mount -t iso9660 "${GAMENAME}" "${GAMEISOMOUNT}"; then
        if ! mount -t udf "${GAMENAME}" "${GAMEISOMOUNT}"; then
            rmdir "${GAMEISOMOUNT}"
            return 1
        fi
    fi

    createWineDirectory "${WINEPOINT}"

    if mkdir -p "${WINEPOINT}/dosdevices" && rm -f "${WINEPOINT}/dosdevices/d:" && ln -sf "${GAMEISOMOUNT}" "${WINEPOINT}/dosdevices/d:"; then
	    WINEPREFIX=${WINEPOINT} ${CMD_PREFIX} "${WINE}" explorer "d:"
	    rm -f "${WINEPOINT}/dosdevices/d:"
    fi

    waitWineServer 0
    createAutorunCmd "${WINEPOINT}" "drive_c/P*"

}

wine2squashfs() {
    #GAMENAME is an .wine directory here, compress DIR -> FILE.wsquasfs
    GAMEDIR="$1"
    SQUASHFSFILE="$2"
    mksquashfs "${GAMEDIR}" "${SQUASHFSFILE}" -comp zstd || return 1
    echo "Created file: $(basename "${SQUASHFSFILE}") <- Dir: ${GAMEDIR}"
    return 0
}

wine2winetgz() {
    GAMEDIR="$1"
    WINETGZFILE="$2"
    echo "Building compressed file: $(basename "${WINETGZFILE}") <- ${GAMEDIR}"
    (cd "${GAMEDIR}" && tar cf - * | gzip -c > "${WINETGZFILE}") && echo "File: $(basename "${WINETGZFILE}") build..." || return 1
    return 0
}

requestFileSystem() {
    #@DEVS Caller function, current function is always inserted as element 0, called one is therefore 1
    local i ii
    echo "------ ${FUNCNAME[0]} called from ${FUNCNAME[1]} ------"
    for i in "$@"; do
        [ ! -e "$i" ] && echo "${FUNCNAME[1]}: $i does not exists" && continue
        ii=$(df -PTh "$i" | awk 'END{print $2}')
        [ -d "$i" ] && printf "%s: Directory %s uses filesystem %s" "${FUNCNAME[1]}" "$i" "$ii"
        [ -f "$i" ] && printf "%s: File      %s uses filesystem %s" "${FUNCNAME[1]}" "$i" "$ii"
        [ -L "$i" ] && printf "\n\t \\----> Symlinked to: " && readlink "$i" || echo
    done
}

cleanAndExit() {
    RESNEW=$(batocera-resolution currentMode)
    if [[ "${RESNEW}" != "${G_RESCUR}" ]]; then
        batocera-resolution setMode "${G_RESCUR}"
    fi

    if [[ -e "${GST_REGISTRY_1_0}" ]]; then
        rm -f "${GST_REGISTRY_1_0}"
    fi

    case "${GAMEEXT,,}" in
        "iso")
            # try to clean the cdrom
            [[ -n "${GAMEISOMOUNT}" ]] && [[ -d "${GAMEISOMOUNT}" ]] && safe_umount "${GAMEISOMOUNT}" || return 1
            [[ -n "${GAMEISOMOUNT}" ]] && [[ -d "${GAMEISOMOUNT}" ]] && rm -rf "${GAMEISOMOUNT}"
            ;;
        "wsquashfs")
            # Safely unmount and clean up
            [[ -n "${WINEPOINT}" ]] && [[ -d "${WINEPOINT}" ]] && safe_umount "${WINEPOINT}" || return 1
            [[ -n "${SQUASHFSPOINT}" ]] && [[ -d "${SQUASHFSPOINT}" ]] && safe_umount "${SQUASHFSPOINT}" || return 1

            # Remove directories if they exist
            [[ -n "${SQUASHFSPOINT}" ]] && [[ -d "${SQUASHFSPOINT}" ]] && rm -rf "${SQUASHFSPOINT}"
            [[ -n "${WORKPOINT}" ]] && [[ -d "${WORKPOINT}" ]] && rm -rf "${WORKPOINT}"
            [[ -n "${WINEPOINT}" ]] && [[ -d "${WINEPOINT}" ]] && rm -rf "${WINEPOINT}"
            ;;
    esac
    echo "WineServer was ${SECONDS}s active"
    return $?
}

init_wine() {
    # Check the system's display mode once at the start
    SYSTEM_DISPLAY_MODE=$(batocera-resolution getDisplayMode)

    ## Wine detection
    WINE_RUNNER="$(/usr/bin/batocera-settings-get windows.wine-runner)"
    [[ -z "$WINE_RUNNER" ]] && WINE_RUNNER="wine-tkg"

    WINE_VERSION="$(get_setting wine-runner "${SYSTEM}" "${ROMGAMENAME}")"
    # to help with the transition from previous runners.
    [[ -z "$WINE_VERSION" ]] && WINE_VERSION="$(get_setting core "${SYSTEM}" "${ROMGAMENAME}" || echo "${WINE_RUNNER}")"

    if [[ "${WINE_VERSION}" == "lutris" ]]; then
        WINE_VERSION="wine-tkg"
    elif [[ "${WINE_VERSION}" == "proton" ]]; then
        WINE_VERSION="wine-proton"
    fi
    echo "*** Chosen WINE runner is ${WINE_VERSION} ***"

    ## Wine executables
    DIR="$(find_wine_dir "$WINE_VERSION")"
    if [[ $? -eq 0 ]]; then
        WINE_VERSION="$(update_wine_version "$WINE_VERSION")"
    else
        echo "can't find WINE version ${WINE_VERSION} directory, you should change the runner"
        exit 1
    fi

    echo "*** Directory checks complete, WINE runner is ${WINE_VERSION} ***"
    USER_DIR="/userdata/system/wine"
    # check 32-bit wine path
    if [[ -e "${DIR}/${WINE_VERSION}/lib/wine/i386-unix/wine" ]]; then
        WINE="${DIR}/${WINE_VERSION}/lib/wine/i386-unix/wine"
    else
        WINE="${DIR}/${WINE_VERSION}/bin/wine"
    fi
    # check 64-bit wine64 path
    if [[ -e "${DIR}/${WINE_VERSION}/lib/wine/x86_64-unix/wine64" ]]; then
        WINE64="${DIR}/${WINE_VERSION}/lib/wine/x86_64-unix/wine64"
    else
        WINE64="${DIR}/${WINE_VERSION}/bin/wine64"
    fi
    WINESERVER="${DIR}/${WINE_VERSION}/bin/wineserver"
    MSIEXEC="${DIR}/${WINE_VERSION}/bin/msiexec"
    WINETRICKS="${DIR}/winetricks"

    # Check lib64 directory
    if [[ -e "${DIR}/${WINE_VERSION}/lib64/wine" ]]; then
        WINE_LIB64_DIR="${DIR}/${WINE_VERSION}/lib64/wine"
    else
        WINE_LIB64_DIR="${DIR}/${WINE_VERSION}/lib/wine"
    fi
    # Check lib32 directory
    if [[ -e "${DIR}/${WINE_VERSION}/lib32/wine" ]]; then
        WINE_LIB32_DIR="${DIR}/${WINE_VERSION}/lib32/wine"
    else
        WINE_LIB32_DIR="${DIR}/${WINE_VERSION}/lib/wine"
    fi

    ## Export Wine libs
    PATH=$PATH:PATH=$PATH:${DIR}/${WINE_VERSION}/bin
    export LD_LIBRARY_PATH="/lib32:${WINE_LIB32_DIR}/i386-unix:/lib:/usr/lib:${WINE_LIB64_DIR}/x86_64-unix"
    export GST_PLUGIN_SYSTEM_PATH_1_0="/usr/lib/gstreamer-1.0:/lib32/gstreamer-1.0"
    export GST_REGISTRY_1_0="/userdata/system/.cache/gstreamer-1.0/registry.x86_64.bin:/userdata/system/.cache/gstreamer-1.0/registry..bin"
    export LIBGL_DRIVERS_PATH="/lib32/dri:/usr/lib/dri"
    export WINEDLLPATH="${WINE_LIB32_DIR}/i386-windows:${WINE_LIB64_DIR}/x86_64-windows"
    # hum pw 0.2 and 0.3 are hardcoded, not nice
    export SPA_PLUGIN_DIR="/usr/lib/spa-0.2:/lib32/spa-0.2"
    export PIPEWIRE_MODULE_DIR="/usr/lib/pipewire-0.3:/lib32/pipewire-0.3"

    # safe old resolution, for bringing it back properly after WINE closes, cleanAndExit()
    G_RESCUR=$(batocera-resolution currentMode)
}

###### MAIN #######
trap stopWineServer SIGHUP SIGINT SIGTERM

#Init WINE Folders and Extension only if parameters are correct and a system is setted
[[ -z "${SYSTEM}" ]] && SYSTEM="~NO SYSTEM~"
[[ -z "${ACTION}" ]] && ACTION="~NO ACTION~"

case "${ACTION}" in
   "stop")
        echo "Stop called from Sunbeam: Outside World"
        PID=$(pgrep -f -o $0)
        kill -1 $(pgrep -P $PID)
        kill -1 $PID
        exit 0
   ;;

# case selections will provide 2 variables here, GAMENAME and WINEPOINT
   "play")
   init_wine
	case "${GAMEEXT,,}" in
	    "wine")
		requestFileSystem "${GAMENAME}"
		play_wine "${GAMENAME}" "${GAMENAME}"
		;;
	    "pc")
		requestFileSystem "${GAMENAME}" "${WINE_BOTTLE_DIR}/${WINE_VERSION}/${ROMGAMENAME}.wine"
		play_pc "${GAMENAME}" "${WINE_BOTTLE_DIR}/${WINE_VERSION}/${ROMGAMENAME}.wine"
		;;
	    "exe")
		requestFileSystem "${GAMENAME}" "${WINE_BOTTLE_DIR}/${WINE_VERSION}/${ROMGAMENAME}.wine"
		play_exe "${GAMENAME}" "${WINE_BOTTLE_DIR}/${WINE_VERSION}/${ROMGAMENAME}.wine"
		;;
#	    "iso")
#		play_iso "${GAMENAME}"
#		;;
	    "wsquashfs")
		#Arguments, ROMNAME, WINEPREFIX as squashfs, SAVEDIR, WORKDIR
		requestFileSystem "${GAMENAME}" "${WINE_BOTTLE_DIR}/${WINE_VERSION}/${ROMGAMENAME}.wine"
		play_squashfs "${GAMENAME}" "/var/run/wine/${ROMGAMENAME}" "${WINE_BOTTLE_DIR}/${WINE_VERSION}/${ROMGAMENAME}.wine" "${WINE_BOTTLE_DIR}/${ROMGAMENAME}.work"
		;;
	    "wtgz")
		requestFileSystem "${GAMENAME}" "${WINE_BOTTLE_DIR}/${WINE_VERSION}/${ROMGAMENAME}.wine"
		play_winetgz "${GAMENAME}" "${WINE_BOTTLE_DIR}/${WINE_VERSION}/${ROMGAMENAME}.wine"
		;;
	    *)
		echo "unknown extension ${GAMEEXT}" >&2
	esac
	;;

    "install")
    init_wine && requestFileSystem "${GAMENAME}"
	case "${GAMEEXT,,}" in
	    "exe"|"msi")
		#Winepoint with stripped extension, add current date_time to avoid duplicates
		install_exe_msi "${GAMEEXT,,}" "${GAMENAME}" "${G_ROMS_DIR}/$(date +%y%m%d-%H%M%S)_${ROMGAMENAME%.*}.wine"
		;;
	    "iso")
		#Parsing Gamename, Winepoint with stripped extension, add current date_time to avoid duplicates
		install_iso "${GAMENAME}" "${G_ROMS_DIR}/$(date +%y%m%d-%H%M%S)_${ROMGAMENAME%.*}.wine"
		;;
	    *)
		echo "unknown extension ${GAMEEXT}" >&2
	esac
	;;

    "tricks")
    init_wine && requestFileSystem "${GAMENAME}"
	case "${GAMEEXT,,}" in
	    "wine")
		trick_wine "${GAMENAME}" "${GAMENAME}" "${TRICK}"
		;;
	esac
	;;

    "wine2squashfs")
	#Parsing Gamename, location and name of compressed file
	wine2squashfs "${GAMENAME}" "${G_ROMS_DIR}/${ROMGAMENAME%.*}.wsquashfs"
	exit $?
	;;

    "wine2winetgz")
	wine2winetgz "${GAMENAME}" "${G_ROMS_DIR}/${ROMGAMENAME%.*}.wtgz"
	exit $?
	;;

    "autorun"|"autorun-list"|"autorun-count")
        # Create autorunfile, works only in SSH mode, recommended is arg1=gamepath, arg2=searchmask
        # It's held small: "batocera-wine windows autorun . bin" will search current directory in dir bin
        [[ -z "${GAMENAME}" ]] && GAMENAME="$PWD"
        [[ -z "${AUTORUN_FILEMASK}" ]] && AUTORUN_FILEMASK="."
        pushd "${GAMENAME}" &> /dev/null || { echo "Error: Can't enter dir '${GAMENAME}'" >&2; exit 1; }
        GAMENAME="$PWD"
        createAutorunCmd "${GAMENAME}" "${AUTORUN_FILEMASK}" || exit 1
        [[ ${#AUTORUN_FOUNDEXE[@]} -eq 0 ]] && { ! [[ "${ACTION}" == "autorun-count" ]] && echo "Error: No windows executable found in '${GAMENAME}/${AUTORUN_FILEMASK}'" >&2 || echo 0; exit 2; }
        #show only list of exes if autorun-list/count is set otherwise...
        [[ "${ACTION}" == "autorun-list" ]]  && { printf '%s\n' "${AUTORUN_FOUNDEXE[@]}"; exit 0; }
        [[ "${ACTION}" == "autorun-count" ]] && { printf '%s\n' "${AUTORUN_FOUNDEXE[@]}" | wc -l; exit 0; }
        #...show list with entries numbers to select
        if [[ ${#AUTORUN_FOUNDEXE[@]} -gt 1 ]]; then
            echo "Found ${#AUTORUN_FOUNDEXE[@]} files in ${GAMENAME}"
            echo "0) Enter 0 or CTRL+C to abort creation of autorun.cmd"
            for i in "${AUTORUN_FOUNDEXE[@]}"; do
                let ii++
                echo -e "$ii) $(basename "$i") --> $(dirname "$i")"
            done
            echo; read -p "Select entry to write to autorun.cmd: " ii
        else
            ii=1
        fi
        [[ $ii -eq 0 ]] && exit 2 #0 selected, abort
        [[ -e autorun.cmd ]] && echo "File exists: Creating Backup!" && mv --backup=t autorun.cmd autorun.cmd.bak
        (
            echo "DIR=$(dirname "${AUTORUN_FOUNDEXE[$ii-1]}")"
            echo "CMD=\"$(basename "${AUTORUN_FOUNDEXE[$ii-1]}")\""
        ) > autorun.cmd
        echo "Written: ${GAMENAME}/autorun.cmd"
        popd > /dev/null
        exit 0
    ;;

    *)
        echo "For system <${SYSTEM}> action <${ACTION}> detected" >&2
        echo
        echo "${0} windows play          <game>.iso"             >&2
        echo "${0} windows play          <game>.exe"             >&2
        echo "${0} windows play          <game>.pc"              >&2
        echo "${0} windows play          <game>.wine"            >&2
        echo "${0} windows play          <game>.wsquashfs"       >&2
        echo "${0} windows play          <game>.wtgz"            >&2
        echo "${0} windows install       <game>.exe"             >&2
        echo "${0} windows install       <game>.iso"             >&2
        echo "${0} windows install       <game>.msi"             >&2
        echo "${0} windows tricks        <game>.wine directplay" >&2
        echo "${0} windows wine2squashfs <game.wine>"            >&2
        echo "${0} windows wine2winetgz  <game.wine>"            >&2
        echo "${0} windows autorun       <game>.*    drive_c/P*" >&2
        echo "${0} windows autorun-list  <game>"                 >&2
        echo "${0} windows autorun-count <game>"                 >&2
        echo "${0} windows stop"                                 >&2
        exit 1
esac

cleanAndExit $?
exit $?
